/*
 * Copyright:  2018 smarabbit studio.
 *
 *  Licensed under the Confluent Community License; you may not use this file
 *  except in compliance with the License.  You may obtain a copy of the License at
 *
 *  http://www.confluent.io/confluent-community-license
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 *  WARRANTIES OF ANY KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations under the License.
 *
 *  @作   者： 黄开晖 (117227773@qq.com)
 *  @日   期:  2019年1月7日
 */

package com.massyframework.beanskin.util.threadlocal;

import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.RejectedExecutionException;

/**
 * {@link BinableExecutorService}, 支持线程变量绑定的ExecutorService.<br>
 * 拦截并创建{@link RunnableWrapper}实现对线程变量的注入。
 */
public class BinableExecutorService extends ExecutorServiceWrapper {

    /**
     * 构造方法
     * @param executorService {@link ExecutorService}
     */
    public BinableExecutorService(ExecutorService executorService) {
        super(executorService);
    }

    /**
     * Executes the given command at some time in the future.  The command
     * may execute in a new thread, in a pooled thread, or in the calling
     * thread, at the discretion of the {@code Executor} implementation.
     *
     * @param command the runnable task
     * @throws RejectedExecutionException if this task cannot be
     *                                    accepted for execution
     * @throws NullPointerException       if command is null
     */
    @Override
    public void execute(Runnable command) {
    	
        super.execute(this.createRunableWrapper(command));
    }

    /**
     * Submits a Runnable task for execution and returns a Future
     * representing that task. The Future's {@code get} method will
     * return the given result upon successful completion.
     *
     * @param task   the task to submit
     * @param result the result to return
     * @return a Future representing pending completion of the task
     * @throws RejectedExecutionException if the task cannot be
     *                                    scheduled for execution
     * @throws NullPointerException       if the task is null
     */
    @Override
    public <T> Future<T> submit(Runnable task, T result) {
        return super.submit(this.createRunableWrapper(task), result);
    }

    /**
     * Submits a Runnable task for execution and returns a Future
     * representing that task. The Future's {@code get} method will
     * return {@code null} upon <em>successful</em> completion.
     *
     * @param task the task to submit
     * @return a Future representing pending completion of the task
     * @throws RejectedExecutionException if the task cannot be
     *                                    scheduled for execution
     * @throws NullPointerException       if the task is null
     */
    @Override
    public Future<?> submit(Runnable task) {
        return super.submit(this.createRunableWrapper(task));
    }

    /**
     * 创建{@link Runnable}封装器，在{@link Runnable#run()}绑定线程变量。
     * @param task {@link Runnable}, 待运行的任务
     * @return {@link Runnable}
     */
    private Runnable createRunableWrapper(Runnable task){
        ThreadLocalBinder binder =
                ThreadLocalUtils.createThreadLocalBinder();
        RunnableWrapper wrapper =
                new RunnableWrapper(task, binder);
        return wrapper;
    }

    class RunnableWrapper implements Runnable {

        private final Runnable target;
        private final ThreadLocalBinder binder;

        /**
         * 构造方法
         * @param runnable {@link Runnable}
         * @param binder {@link ThreadLocalBinder}
         */
        public RunnableWrapper(Runnable runnable, ThreadLocalBinder binder){
            this.target = Objects.requireNonNull(
                    runnable, "\"runnable\" cannot be null.");
            this.binder = binder;
        }


        /**
         * When an object implementing interface <code>Runnable</code> is used
         * to create a thread, starting the thread causes the object's
         * <code>run</code> method to be called in that separately executing
         * thread.
         * <p>
         * The general contract of the method <code>run</code> is that it may
         * take any action whatsoever.
         *
         * @see Thread#run()
         */
        @Override
        public void run() {
            if (this.binder != null){
                this.binder.bind();
            }

            this.target.run();
        }
    }
}
